import { Dict, ZPackOptions, PageTemplate } from './Types';
import File from './file/File';
import { Transform } from 'stream';
import Assembly from './Assembly';
import ExternalFile from './file/ExternalFile';
import Loader from './loaders/Loader';

const StandardModules = ['assert', 'buffer', 'child_process', 'cluster', 'console', 'constants', 'crypto',
    'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https', 'module', 'net', 'os', 'path', 'punycode',
    'process', 'querystring', 'readline', 'repl', 'stream', '_stream_duplex', '_stream_passthrough',
    '_stream_readable', '_stream_transform', '_stream_writable', 'string_decoder', 'sys', 'timers', 'tls',
    'tty', 'url', 'util', 'vm', 'zlib', 'ZPackStub', 'v8'
];

const defaultOptions: ZPackOptions = {
    loaders: {
        '.js': 'JsLoader',
        '.jsx': 'JsLoader',
        '.ts': 'TsLoader',
        '.tsx': 'TsLoader',
        '.json': 'JsonLoader',
        '.css': 'CssLoader',
        '.less': 'LessLoader'
    },
    ext: ['.js', '.jsx', '.json', '.ts', '.tsx', '.less'],
    host: '',
    avoidError: false,
    external: {},
    encoding: 'UTF-8',
    rootPath: '',
    transformLibrary: false,
    babel: {
        'presets': [],
        'plugins': [
            'babel-plugin-transform-es2015-modules-commonjs',
            'babel-plugin-transform-export-extensions',
            'babel-plugin-transform-class-properties',
            'babel-plugin-transform-decorators-legacy',
            'babel-plugin-transform-object-rest-spread'
        ]
    },
    pageTemplate: [],
    assembly: 'single',
    needBootFile: true,
    printLoad: true,
    printRead: true,
    printWrite: true,
    preload: []
};

export default class Context {
    public options: ZPackOptions;
    public defaultLoader = new Loader();
    public loaders: Dict<Loader> = {};
    public moduleMetaCache: Dict<ModuleMeta> = {};

    public files: Dict<File> = {};
    public loadingFiles: File[] = [];

    public transform?: Transform;

    public assembly: Assembly;

    public bootDependencies: File[] = [];

    public pageTemplate: PageTemplate[] = [];

    constructor(options: Partial<ZPackOptions>) {
        this.options = Object.assign({}, defaultOptions, options);
        for (let i in this.options.loaders) {
            if (this.options.loaders.hasOwnProperty(i)) {
                this.loaders[i.toLocaleLowerCase()] = this.obtainLoader(this.options.loaders[i]);
            }
        }
        if (typeof this.options.assembly === 'string') {
            this.assembly = Assembly.create(this, this.options.assembly);
        } else {
            this.assembly = this.options.assembly;
        }
        this.pageTemplate = this.options.pageTemplate.map(e => Object.assign({}, e, {
            match: typeof e.match === 'string' ? new RegExp(e.match) : e.match
        }));

        for (let id of StandardModules) {
            this.files[id] = new ExternalFile(this, FileType.system, id);
        }
        for (let id of Object.keys(this.options.external)) {
            this.files[id] = new ExternalFile(this, FileType.external, id);
        }
    }

    obtainLoader(loader: Loader | string): Loader {
        if (typeof loader === 'string') {
            let l = require('./loaders/' + loader);
            if (typeof l === 'function') {
                return new l();
            }
            return new l.default();
        } else if (loader) {
            return loader;
        } else {
            return this.defaultLoader;
        }
    }
}